#include "PseudoClassicLevel.h"
#include "Util.h"
#include "Tile.h"
#include "Wall.h"
#include "Door.h"
//#include <iostream>

PseudoClassicLevel::PseudoClassicLevel(int x, int y, int levelNum, int dlevel, Dungeon* dungeon) : Level(x, y, levelNum, dlevel, dungeon) {
	generateLevel();
	placePortal(true, 0);
}

PseudoClassicLevel::PseudoClassicLevel(int x, int y, Level* level) : Level(x, y, level) {
	generateLevel();
	for(int i = 0; i < level->getNumPortals(false); i++) {
		placePortal(true, i);
	}
}

PseudoClassicLevel::~PseudoClassicLevel() {
}

void PseudoClassicLevel::generateLayout() {
	// initialize tile map with normal tiles
	m_map = new Tile**[m_cols];
	for(int i = 0; i < m_cols; i++) {
		m_map[i] = new Tile*[m_rows];
		for(int j = 0; j < m_rows; j++) {
			m_map[i][j] = new Tile(TT_NORMAL, float(i), float(j), this);
		}
	}
	
	// initialize obstacle map that relates to which obstacles will be placed on tiles
	// 2D array
	// first col - x
	// second col - y
	// value - obstacle type
	//   0 = floor (no obstacle)
	//   1 = wall
	//   2 = door (if applicable)
	int** walls = new int*[m_cols];
	for(int i = 0; i < m_cols; i++) {
		walls[i] = new int[m_rows];
		for(int j = 0; j < m_rows; j++) {
			// start with all walls
			walls[i][j] = 1;
		}
	}

	std::vector<int*> rooms;
	// select a random number of rooms, should be based rows/cols instead of hardcoded
	int numRooms = x_between(15, 30);
	// for each room, try to create the room by carving out walls and replacing them with floors
	for(int i = 0; i < numRooms; i++) {
		// pick random width/height for room
		int roomRandX = x_between(4,12);
		int roomRandY = x_between(4,12);
		int roomAttempts = 0; // number of times the room was attempted to be placed
		do {
			// pick random location for bottom left corner of room (excludes tiles on the outermost edges of map)
			int randX = rand_plus_one(m_cols-roomRandX-1);
			int randY = rand_plus_one(m_rows-roomRandY-1);
			bool createRoom = true;
			// check if this room will overlap with an existing room
			// a more strict check would check 1 tile above and below the min/max
			// in order to ensure at least a 1 tile barrier between rooms
			// this could be smarter by instead only checking x/y/width/height
			// of rooms previous created
			for(int j = randX; j < randX+roomRandX && createRoom; j++) {
				for(int k = randY; k < randY+roomRandY && createRoom; k++) {
					if(walls[j][k] != 1)
						createRoom = false;
				}
			}
			// if there's no overlap, create the room and store it in the list of rooms
			if(createRoom) {
				for(int j = randX; j < randX+roomRandX && createRoom; j++) {
					for(int k = randY; k < randY+roomRandY && createRoom; k++) {
						walls[j][k] = 0;
					}
				}
				roomAttempts = 5;
				int* room = new int[4];
				room[0] = randX;
				room[1] = randY;
				room[2] = roomRandX;
				room[3] = roomRandY;
				rooms.push_back(room);
			}
			roomAttempts++;
		} while (roomAttempts < 5); // if no valid tile found in 5 attempts, skip it
	}
	// select a random number of hallways
	int numPassages = x_between(numRooms-3, numRooms+5);
	// keep a list of whether or not each room has at least 1 hallway going to it
	bool* roomHasPassage = new bool[int(rooms.size())];
	int roomPassageCounter = 0; // the number of rooms that have at least 1 hallway
	for(int i = 0; i < int(rooms.size()); i++)
		roomHasPassage[i] = false;
	// loop until the maximum number of hallways is met or until all rooms have at least 1 hallway
	for(int i = 0; i < numPassages || roomPassageCounter != int(rooms.size()); i++) {
		// select 2 random rooms to connect
		int randRoom1 = rand(int(rooms.size()));
		int randRoom2;
		do {
			randRoom2 = rand(int(rooms.size()));
		} while (randRoom1 == randRoom2);
		// mark each room as having a hallway
		if(!roomHasPassage[randRoom1]) {
			roomHasPassage[randRoom1] = true;
			roomPassageCounter++;
		}
		if(!roomHasPassage[randRoom2]) {
			roomHasPassage[randRoom2] = true;
			roomPassageCounter++;
		}
		int* room1 = rooms[randRoom1];
		int* room2 = rooms[randRoom2];
		// select a random tile in each room to be used as the start/end location for the hallway
		int currX = room1[0]+rand(room1[2]), currY = room1[1]+rand(room1[3]);
		int endX = room2[0]+rand(room2[2]), endY = room2[1]+rand(room2[3]);
		// loop until the current location is not equal to the end location
		while(currX != endX || currY != endY) {
			// pick a random direction (horizontal or vertical)
			if(one_in(2) && currX != endX) {
				// pick a random length for this segment of the hallway from 1 to total distance remaining
				int distance = rand_plus_one(int(std::abs(double(currX-endX))));
				// and then carve out the walls in the way
				for(int i = 0; i < distance; i++) {
					if(currX < endX)
						walls[currX++][currY] = 0;
					else
						walls[currX--][currY] = 0;
				}
			} else if(currY != endY) {
				int distance = rand_plus_one(int(std::abs(double(currY-endY))));
				for(int i = 0; i < distance; i++) {
					if(currY < endY)
						walls[currX][currY++] = 0;
					else
						walls[currX][currY--] = 0;
				}
			}
		}
	}

	// add doors around edges of room if pattern is found (, represents current tile being checked):
	// case1  case2
	// #.#    ###
	// #,# or .,.
	// #.#    ###
	// 50% chance to add a door if target tile is a floor
	// 12.5% chance to add a door if target tile is a wall
	// percentages are magic numbers at the moment, need to fix that eventually
	if(one_in(2)) {
		for(int i = 0; i < int(rooms.size()); i++) {
			int* room = rooms[i];
			int x1 = room[0]-1, x2 = room[0]+room[2], y1 = room[1]-1, y2 = room[1]+room[3];
			// case 1, check tiles on top/bottom rows of room
			for(int x = x1+1; x < x2-1; x++) {
				// bottom row
				if(y1 > 0 && y1 < m_rows-1 && walls[x-1][y1] == 1 && walls[x+1][y1] == 1 && walls[x][y1-1] == 0 && walls[x][y1+1] == 0 && one_in(2))
					if(walls[x][y1] == 0 || one_in(4))
						walls[x][y1] = 2;
				// top row
				if(y2 > 0 && y2 < m_rows-1 && walls[x-1][y2] == 1 && walls[x+1][y2] == 1 && walls[x][y2-1] == 0 && walls[x][y2+1] == 0 && one_in(2))
					if(walls[x][y2] == 0 || one_in(4))
						walls[x][y2] = 2;
			}
			// case 2, check tiles on left/right columns of room
			for(int y = y1+1; y < y2-1; y++) {
				// left column
				if(x1 > 0 && x1 < m_cols-1 && walls[x1-1][y] == 0 && walls[x1+1][y] == 0 && walls[x1][y-1] == 1 && walls[x1][y+1] == 1 && one_in(2))
					if(walls[x1][y] == 0 || one_in(4))
						walls[x1][y] = 2;
				// right column
				if(x2 > 0 && x2 < m_cols-1 && walls[x2-1][y] == 0 && walls[x2+1][y] == 0 && walls[x2][y-1] == 1 && walls[x2][y+1] == 1 && one_in(2))
					if(walls[x2][y] == 0 || one_in(4))
						walls[x2][y] = 2;
			}
		}
	}
	// print ASCII representation of level in console
	/*for(int j = m_rows-1; j >= 0; j--) {
		for(int i = 0; i < m_cols; i++) {
			switch(walls[i][j]) {
			case 0:
				std::cout << ".";
				break;
			case 1:
				std::cout << "#";
				break;
			case 2:
				std::cout << "/";
				break;
			}
		}
		std::cout << std::endl;
	}*/

	// add obstacles to actual tile map
	for(int i = 0; i < m_cols; i++) {
		for(int j = 0; j < m_rows; j++) {
			if(walls[i][j] == 1)
				m_map[i][j]->addObstacle(new Wall());
			else if(walls[i][j] == 2)
				m_map[i][j]->addObstacle(new Door(LockType(rand(NUM_LOCK_TYPES)), one_in(3))); // give doors random lock type and random locked status
		}
	}

	// free all dynamically allocated memory
	for(unsigned int i = 0; i < rooms.size(); i++)
		delete[] rooms[i];
	rooms.clear();
	delete[] roomHasPassage;
	
	for(int i = 0; i < m_cols; i++) {
		delete[] walls[i];
	}
	delete[] walls;
}
